/**
 *    Copyright (c) [2022] [中和农信]
 *    rabbit-mq-plus is licensed under Mulan PSL v2.
 *    You can use this software according to the terms and conditions of the Mulan PSL v2.
 *    You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 *    THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *    See the Mulan PSL v2 for more details.
 *
 */
package com.cfpamf.athean.rabbit.mq.plus.service.impl;

import java.util.Objects;

import javax.annotation.Resource;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cfpamf.athean.rabbit.mq.plus.common.constant.BusinessConstants;
import com.cfpamf.athean.rabbit.mq.plus.common.enums.ProcessStatusEnum;
import com.cfpamf.athean.rabbit.mq.plus.common.utils.RabbitMqUtils;
import com.cfpamf.athean.rabbit.mq.plus.domain.dto.RabbitMqPlusEventTaskDTO;
import com.cfpamf.athean.rabbit.mq.plus.domain.dto.RabbitMqPlusMessageDTO;
import com.cfpamf.athean.rabbit.mq.plus.producer.core.RabbitMqPlusProducerResultTemplate;
import com.cfpamf.athean.rabbit.mq.plus.service.RabbitMqPlusEventTaskService;
import com.cfpamf.athean.rabbit.mq.plus.service.RabbitMqPlusProducerService;
import com.cfpamf.athena.utils.DateUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 类RabbitMqPlusProducerServiceImpl.java的实现描述：
 *
 * @author Andy（陈洪桥）2022年11月26 16:00
 */
@Slf4j
@Service
public class RabbitMqPlusProducerServiceImpl implements RabbitMqPlusProducerService {

    @Resource
    private RabbitMqPlusEventTaskService rabbitMqPlusEventTaskService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RabbitMqPlusProducerResultTemplate rabbitMqPlusProducerResultTemplate;

    /**
     * 消息发送
     */
    @Override
    @Transactional
    public void sendMessage(final RabbitMqPlusMessageDTO rabbitMqPlusMessageDTO, final int retryCount) {
        try {
            RabbitMqPlusEventTaskDTO task = rabbitMqPlusEventTaskService.queryByEventNoAndEventCode(
                rabbitMqPlusMessageDTO.getEventNo(), rabbitMqPlusMessageDTO.getEventTypeCode());
            if (Objects.isNull(task)) {
                log.info("[rabbitMqPlus]生产者消息发送，记录未查到，拒绝发送，exchange {} eventNo {} eventTypeCode {}",
                    rabbitMqPlusMessageDTO.getExchange(), rabbitMqPlusMessageDTO.getEventNo(),
                    rabbitMqPlusMessageDTO.getEventTypeCode());
                return;
            }
            if (ProcessStatusEnum.notTodo(task.getStatusCode()) || ProcessStatusEnum.notFail(task.getStatusCode())) {
                log.info("[rabbitMqPlus]生产者消息发送，记录状态不为待发送或发送失败，拒绝发送，exchange {} eventNo {} eventTypeCode {}",
                    rabbitMqPlusMessageDTO.getExchange(), rabbitMqPlusMessageDTO.getEventNo(),
                    rabbitMqPlusMessageDTO.getEventTypeCode());
                return;
            }
            RabbitMqUtils.sendMessage(rabbitTemplate, rabbitMqPlusMessageDTO.getExchange(),
                rabbitMqPlusMessageDTO.getMessageId(), rabbitMqPlusMessageDTO.getData(),
                rabbitMqPlusProducerResultTemplate, rabbitMqPlusMessageDTO.getEventNo(),
                rabbitMqPlusMessageDTO.getEventTypeCode(), retryCount);
            // 消息已投递，未拿到投递结果，更新为处理中
            this.updateProcessingByEventNoAndEventCode(rabbitMqPlusMessageDTO.getEventNo(),
                rabbitMqPlusMessageDTO.getEventTypeCode(), retryCount);
            log.info("[rabbitMqPlus]生产者消息发送中，exchange {} eventNo {} eventTypeCode {}",
                rabbitMqPlusMessageDTO.getExchange(), rabbitMqPlusMessageDTO.getEventNo(),
                rabbitMqPlusMessageDTO.getEventTypeCode());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("[rabbitMqPlus]生产者消息发送，异常，eventNo {} eventTypeCode {}", rabbitMqPlusMessageDTO.getEventNo(),
                    rabbitMqPlusMessageDTO.getEventTypeCode(), e);
            }
            // 消息发送失败，更新重试次数，计算下次执行时间，设置消息发送状态为失败
            this.updateFailByEventNoAndEventCode(rabbitMqPlusMessageDTO.getEventNo(),
                rabbitMqPlusMessageDTO.getEventTypeCode(), retryCount);
        }
    }

    @Transactional
    public void retrySendMessage(final RabbitMqPlusMessageDTO rabbitMqMessageDTO, int retryCount) {
        try {
            retryCount = retryCount++;
            this.sendMessage(rabbitMqMessageDTO, retryCount);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("[rabbitMqPlus]retrySendMessage消息补偿失败，{} {} msg：{}", rabbitMqMessageDTO.getEventNo(),
                    rabbitMqMessageDTO.getEventTypeCode(), e.getMessage());
            }
            // 消息发送失败，更新重试次数，计算下次执行时间，设置消息发送状态为失败
            this.updateFailByEventNoAndEventCode(rabbitMqMessageDTO.getEventNo(), rabbitMqMessageDTO.getEventTypeCode(),
                retryCount);
        }
    }

    @Transactional
    public void updateProcessingByEventNoAndEventCode(final String eventNo, String eventTypeCode,
        final int retryCount) {
        RabbitMqPlusEventTaskDTO updateEventTaskDTO = new RabbitMqPlusEventTaskDTO();
        updateEventTaskDTO.setStatusCode(ProcessStatusEnum.PROCESSING.getCode());
        updateEventTaskDTO.setStatusDesc(ProcessStatusEnum.PROCESSING.getDesc());
        updateEventTaskDTO.setRetryCount(retryCount);
        updateEventTaskDTO.setEventNo(eventNo);
        updateEventTaskDTO.setEventTypeCode(eventTypeCode);
        updateEventTaskDTO.setEventTime(DateUtils.currentTime());
        // 修改DB状态processing
        boolean status = rabbitMqPlusEventTaskService.updateByEventNoAndEventCode(updateEventTaskDTO);
        if (log.isDebugEnabled()) {
            log.debug("[rabbitMqPlus]updateProcessingByEventNoAndEventCode失败，eventNo：{} eventTypeCode：：{}", eventNo,
                eventTypeCode, status);
        }
    }

    @Transactional
    public void updateFailByEventNoAndEventCode(final String eventNo, String eventTypeCode, final int retryCount) {
        RabbitMqPlusEventTaskDTO updateEventTaskDTO = new RabbitMqPlusEventTaskDTO();
        updateEventTaskDTO.setStatusCode(ProcessStatusEnum.FAIL.getCode());
        updateEventTaskDTO.setStatusDesc(ProcessStatusEnum.FAIL.getDesc());
        updateEventTaskDTO.setRetryCount(retryCount);
        updateEventTaskDTO.setEventNo(eventNo);
        updateEventTaskDTO.setEventTypeCode(eventTypeCode);
        updateEventTaskDTO.setEventTime(DateUtils.currentTime());
        // 上次重试次数，如果小于=0，设置为1
        int lastRetryCount = (retryCount - BusinessConstants.ONE) > BusinessConstants.ZERO
            ? retryCount - BusinessConstants.ONE : BusinessConstants.ONE;
        // 消息发送失败，更新重试次数，计算下次执行时间
        updateEventTaskDTO.setNextExecuteTime(DateUtils.addMinute(updateEventTaskDTO.getEventTime(),
            ((BusinessConstants.FIVE * lastRetryCount) * retryCount)));
        // 修改DB状态FAIL
        boolean status = rabbitMqPlusEventTaskService.updateByEventNoAndEventCode(updateEventTaskDTO);
        if (log.isDebugEnabled()) {
            log.debug("[rabbitMqPlus]updateFailByEventNoAndEventCode失败，eventNo：{} eventTypeCode：：{}", eventNo,
                eventTypeCode, status);
        }
    }

    @Transactional
    public void updateDoneByEventNoAndEventCode(final String eventNo, String eventTypeCode) {
        RabbitMqPlusEventTaskDTO updateEventTaskDTO = new RabbitMqPlusEventTaskDTO();
        updateEventTaskDTO.setStatusCode(ProcessStatusEnum.DONE.getCode());
        updateEventTaskDTO.setStatusDesc(ProcessStatusEnum.DONE.getDesc());
        updateEventTaskDTO.setEventNo(eventNo);
        updateEventTaskDTO.setEventTypeCode(eventTypeCode);
        updateEventTaskDTO.setEventTime(DateUtils.currentTime());
        // 修改DB状态DONE
        boolean status = rabbitMqPlusEventTaskService.updateByEventNoAndEventCode(updateEventTaskDTO);
        if (log.isDebugEnabled()) {
            log.debug("[rabbitMqPlus]updateDoneByEventNoAndEventCode失败，eventNo：{} eventTypeCode：：{}", eventNo,
                eventTypeCode, status);
        }
    }

}
